package com.atwisdom.star.common.function;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.*;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.jayway.jsonpath.JsonPath;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * map工具类
 */
@Slf4j
public class AtwisdomMapFunction {

    /**
     * 由于aviator在seq.get执行过程中，遇到结果为null会报错，不能提供返回值，所以自定义方法
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static Object mapGetOrDefault(Map<String, Object> map, String key, Object defaultValue) {
        if (CollectionUtil.isNotEmpty(map)) {
            if (map.get(key) != null) {
                return map.getOrDefault(key, defaultValue);
            } else {
                return defaultValue;
            }
        } else {
            return defaultValue;
        }
    }

    /**
     * 根据以指定字符串开始的key, 找出两个相同的key,并以指定的字符相连接
     * 根据keyStartStr找出Key以keyStartStr开头的，同时key又不以其他
     * 业务上用于查询报工单中有数据，但不是员工所在线体的数据，视为派工单件数
     * @param map1
     * @param map2
     * @param keyStartStr
     * @return
     */
    public static Object multiplyAndSumByKeyStartAndNot(Map<String, Object> map1, Map<String, Object> map2, String keyStartStr, String notKeyStartStr, String joinKey, String concatKey) {
        StringBuilder result = new StringBuilder();
        map1.forEach((k1,v1) -> {
            if (k1.startsWith(keyStartStr) && !k1.startsWith(notKeyStartStr)) {
                result.append(v1 + joinKey + map2.get(k1));
                result.append(concatKey);
            }
        });
        String aviatorResult = "";
        if (result.length() > 1) {
            aviatorResult = result.substring(0, result.length() - 1);
            log.info("multiplyAndSumByKeyStart aviatorResult = {}", aviatorResult);
            return AviatorEvaluator.execute(aviatorResult);
        } else {
            log.info("multiplyAndSumByKeyStart result = 0");
            return 0;
        }
    }

    /**
     * 根据以指定字符串开始的key, 找出两个相同的key,并以指定的字符相连接
     * @param map1
     * @param map2
     * @param keyStartStr
     * @return
     */
    public static Object multiplyAndSumByKeyStart(Map<String, Object> map1, Map<String, Object> map2, String keyStartStr, String joinKey, String concatKey) {
        StringBuilder result = new StringBuilder();
        map1.forEach((k1,v1) -> {
            if (k1.startsWith(keyStartStr)) {
                result.append(v1 + joinKey + map2.get(k1));
                result.append(concatKey);
            }
        });
        String aviatorResult = "";
        if (result.length() > 1) {
            aviatorResult = result.substring(0, result.length() - 1);
            log.info("multiplyAndSumByKeyStart aviatorResult = {}", aviatorResult);
            return AviatorEvaluator.execute(aviatorResult);
        } else {
            log.info("multiplyAndSumByKeyStart result = 0");
            return 0;
        }
    }


    /**
     * 取出两层嵌套的map的值
     * @param mapList
     * @return
     */
    public static Object sumByPath(HashMap mapList, String path) {
        Object readResult = JsonPath.read(mapList, path);
        log.info("readResult = {}", readResult);
        Map paramMap = new HashMap();
        paramMap.put("readResult", readResult);
        Object result = AviatorEvaluator.execute("reduce(readResult, +, 0) ", paramMap);
        log.info("sumByPath AviatorEvaluator Result = {}", readResult);
        return result;
    }


    /**
     * 对map中的值进行排序
     * @param comparator
     * @return
     */
    public static Object sortByKey(Map map, Comparator comparator) {
        Map<String, Integer> treeMap = new TreeMap<>(comparator);
        treeMap.putAll(map);
        // log.info("map = {}", map);
        // log.info("treeMap = {}", treeMap);
        return treeMap;
    }

    /**
     * 对map中的值，按照指定的key提取数据后，进行sum计算
     * @param valueSumKey  做sum计算，获取value时使用的Key
     * @return
     */
    public static Object valueSum(Map<String, List<Map>> map, String valueSumKey) {
        Map<String, Object> resultMap = new TreeMap<>();
        map.forEach((k, v) -> {
            double newValue = v.stream().mapToDouble(d -> Double.valueOf(String.valueOf(d.getOrDefault(valueSumKey, 0d)))).sum();
            resultMap.put(k, newValue);
        });
        return resultMap;
    }

    /**
     * 对map中的值进行分组，即将一个Map<String, List<Map>>  转换为 Map<String, Map>
     * @param groupByKey  分组用到的key
     * @param valueSumKeyList  做sum计算，获取value时使用的Key
     * @return
     */
    public static Object innerGroup(Map<String, List<Map>> map, String groupByKey, List<String> valueSumKeyList) {
        Map<String, Object> resultMap = new TreeMap<>();
        map.forEach((k, v) -> {
            Map<String, Double> newValue = v.stream().collect(Collectors.groupingBy(d -> String.valueOf(d.get(groupByKey)),
                    Collectors.summingDouble(d -> {
                        Double sum = 0d;
                        for (String sumKey : valueSumKeyList) {
                            sum += Double.valueOf(String.valueOf(d.getOrDefault(sumKey, 0d)));
                        }
                        return  sum;
                    })));
            resultMap.put(k, newValue);
        });
        return resultMap;
    }

    /**
     * map中的值为list时，计算 list中的指定字段的sum， 并转换为 Map<String, Object>
     * @param valueSumKeyList  做sum计算，获取value时使用的Key
     * @return
     */
    public static Object mapListSum(Map<String, List<Map>> map, List<String> valueSumKeyList) {
        Map<String, Object> resultMap = new TreeMap<>();
        map.forEach((k, v) -> {
            Double newValue = v.stream().collect(Collectors.summingDouble(d -> {
                Double sum = 0d;
                for (String sumKey : valueSumKeyList) {
                    sum += Double.valueOf(String.valueOf(d.getOrDefault(sumKey, 0d)));
                }
                return sum;
            }));
            resultMap.put(k, newValue);
        });
        return resultMap;
    }


    /**
     * map中的值为list时，计算 list中的指定字段的平均值， 并转换为 Map<String, Object>
     * @param averageKey  做average计算，获取value时使用的Key
     * @return
     */
    public static Object mapListAverage(Map<String, List<JSONObject>> map, String averageKey) {
        Map<String, Object> resultMap = new TreeMap<>();
        map.forEach((k, v) -> {
            Double newValue = v.stream().collect(Collectors.averagingDouble(d -> d.getDouble(averageKey) != null ? d.getDouble(averageKey) : 0d));
            resultMap.put(k, newValue);
        });
        return resultMap;
    }

    /**
     * 对list求平均值
     * @return
     */
    public static Object listAverage(List<Double> list) {
        Double newValue = list.stream().map(d -> Double.valueOf(String.valueOf(d))).collect(Collectors.averagingDouble(d->d));
        return newValue;
    }

    /**
     * 比较两个日期相差的天数
     * @param begin
     * @param end
     * @return
     * 2023-10-01 00:00:00
     */
    public static Object betweenTwoDate(String begin, String end) {
        LocalDateTime beginDate = LocalDateTimeUtil.parse(begin);
        LocalDateTime endDate = LocalDateTimeUtil.parse(end);
        Duration duration = LocalDateTimeUtil.between(beginDate, endDate);
        return duration.toDays();
    }

    /**
     * 返回两个日期间的每一天的日期
     * @param begin
     * @param end
     * @return
     * 2023-10-01 00:00:00
     */
    public static Object everyDayBetweenDate(String begin, String end) {
        List<String> resultList = new ArrayList<>();
        String format = DatePattern.NORM_DATE_PATTERN;
        if (begin.length() > 10) {
            format = DatePattern.NORM_DATETIME_PATTERN;
        }
        LocalDateTime beginDate = LocalDateTimeUtil.parse(begin, format);
        LocalDateTime endDate = LocalDateTimeUtil.parse(end, format);
        while (!LocalDateTimeUtil.isSameDay(beginDate, endDate)) {
            String formatStr = LocalDateTimeUtil.format(beginDate, DatePattern.NORM_DATE_FORMATTER);
            resultList.add(formatStr);
            beginDate = beginDate.plusDays(1L);
        }
        return resultList;
    }

    /**
     * 返回两个日期间的每个小时数
     * @param begin
     * @param end
     * @return
     * 2023-10-01 00:00:00
     */
    public static Object everyHourBetweenDate(String begin, String end) {
        List<Integer> resultList = new ArrayList<>();
        String format = DatePattern.NORM_DATETIME_PATTERN;
        LocalDateTime beginDate = LocalDateTimeUtil.parse(begin, format);
        LocalDateTime endDate = LocalDateTimeUtil.parse(end, format);
        Duration duration = LocalDateTimeUtil.between(beginDate, endDate);
        for (int i = 0; i <= duration.toHours(); i++) {
            resultList.add((i+beginDate.getHour())%24);
        }
        return resultList;
    }



    public static Object groupByAndMax(List<JSONObject> list) {
        Map<Object, BigDecimal> map = list.stream().collect(Collectors.toMap(d -> d.get(""), d -> d.getBigDecimal(""), BigDecimal::max));
        return map;
    }

    /**
     * 分组后，每组取平均值
     * @param list
     * @param groupByKey 分组key
     * @param groupByKeyBegin 对key进行裁剪
     * @param groupByKeyEnd 对key进行裁剪
     * @param averageKey 平均值字段
     * @return
     */
    public static Object groupByAndAverage(List<JSONObject> list, String groupByKey, Integer groupByKeyBegin, Integer groupByKeyEnd, String averageKey) {
        Map<Object, Double> collect = list.stream()
                .filter(a -> a.get(groupByKey) != null)
                .filter(b -> b.get(averageKey) != null)
                .collect(Collectors.groupingBy(a -> {
                            Object key = a.get(groupByKey);
                            if (groupByKeyBegin != null && groupByKeyEnd != null) {
                                key = String.valueOf(key).substring(groupByKeyBegin, groupByKeyEnd);
                            }
                            return key;
                        },
                        Collectors.averagingDouble(a -> Double.parseDouble(a.getString(averageKey)))));
        return collect;
    }

    public static void main(String[] args) throws IllegalAccessException, NoSuchMethodException {
        System.out.println(AviatorEvaluator.execute("math.round(4.0/12.0*100)/100.0"));

        System.out.println(everyHourBetweenDate("2023-10-01 22:00:00", "2023-10-02 23:01:00"));
        System.out.println(everyDayBetweenDate("2023-10-01 00:00:00", "2023-11-01 00:00:00"));

        Map map1 = new HashMap();
        map1.put("a", 3);
        map1.put("b", "1*2");
        map1.put("c", "2*3");
        Map map2 = new HashMap();
        map2.put("a", "1*2");
        map2.put("b", 9);
        map2.put("c", "2*3");

        Map map3 = new HashMap();
        map3.put("map1", map1);
        map3.put("map2", map2);

        Map map4 = new HashMap();
        map4.put("a", 10);

        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("map2", map2);
        paramMap.put("map1", map1);
        paramMap.put("map3", map3);
        paramMap.put("map4", map4);
        AviatorEvaluator.addStaticFunctions("atwisdomMapFunction", AtwisdomMapFunction.class);

        Object result1 = AviatorEvaluator.execute("atwisdomMapFunction.sumByPath(filter(map3, lambda(item)->string.startsWith(item.key,'map') end), '$..a')",
                paramMap);
        System.out.println(result1);
        // Object result2 = AviatorEvaluator.execute("atwisdomMapFunction.mapGetOrDefault(atwisdomMapFunction.mapGetOrDefault(map3, 'map1'), 'a')",
        //         paramMap);
        Object result2 = AviatorEvaluator.execute("atwisdomMapFunction.mapGetOrDefault(atwisdomMapFunction.mapGetOrDefault(map3, 'map1', nil), 'ab', 0)",
                paramMap);
        System.out.println(result2);

        Object sortResult = AviatorEvaluator.execute("atwisdomMapFunction.sortByKey(map2, nil)",
                paramMap);
        System.out.println(sortResult);


        Object forResult = AviatorEvaluator.execute(
                        "let mapA = seq.map();  for k,v in map3 { let map11 = seq.map();" +
                                "for k1, v1 in v {" +
                                "seq.put(map11, k1, 2)" +
                                "}" +
                                "seq.put(mapA, k, map11); } return mapA; ",
                paramMap);
        System.out.println("forResult = " + forResult);

        Object listResult = AviatorEvaluator.execute(
                "let list = seq.list(); seq.add(list, 1); seq.add(list, 1); seq.add(list, 1); return list;",
                paramMap);
        System.out.println("listResult = " + listResult);


    }
}

    // MapMultiplyByKeyFunction